1 module dataframe.csv;
2 import dataframe.common;
3 import dataframe.variant;
4 import dataframe.typed;
5 import std.conv;
6 import std.csv;
7 import std.datetime;
8 import std.exception;
9 import std.range:array, stride,only;
10 import std.stdio;
11 import std.variant;
12 import std.string:isNumeric;
13 import std.typecons:tuple,Tuple;
14 import std.traits;
15 import std.file;
16 
17 private size_t peekCols(string data, char separator=',')
18 {
19 	import std.string:indexOf,split;
20 	auto i=indexOf(data,"\n");
21 	return (i==-1)?0:(data[0..i].split([separator]).length);
22 }
23 private string[] peekHeaderCols(string data, char separator=',')
24 {
25 	import std.string:indexOf,split;
26 	auto i=indexOf(data,"\n");
27 	return (i==-1)?[]:(data[0..i].split([separator]));
28 }
29 
30 DataFrame loadCSV(Malformed errorLevel=Malformed.ignore)(string data, bool hasHeader=false, char separator=',')
31 {
32 //		auto csv=csvReader!(string, errorLevel)(data, separator, quote);
33 	DataFrame ret;
34 	auto csv=csvReader(data,separator); // , separator, quote);
35 	size_t i;
36 	ret.setCellColumns(data.peekCols-1); // since first col is the index value
37 	if (hasHeader)
38 	{
39 		auto cols=peekHeaderCols(data,separator);
40 		ret.setColumnTitles(cols[1..$]);
41 		ret.setIndexTitle(cols[0]);
42 	}
43 	bool firstRow=true;
44 	foreach(row;csv)
45 	{
46 		if (firstRow && hasHeader)
47 		{
48 			firstRow=false;
49 			continue;
50 		}
51 		size_t j;
52 		foreach(col;row)
53 		{
54 			if (j==0)
55 				ret.indexValues~=col.to!KalVariant;
56 			else
57 			{
58 				ret.cellValues.length+=ret.numCols;
59 				ret[i,j]=col;
60 			}
61 			++j;
62 		}
63 		++i;
64 	}
65 	return ret;
66 }
67 
68 DataFrame saveCSV(DataFrame frame, string filename, bool useHeader=true)
69 {
70 	string ret;
71 	if(useHeader)
72 	{
73 		foreach(j;0..frame.numCols)
74 			ret~=frame.columnTitles[j]~",";
75 		ret=ret[0..$-1]~"\n";
76 	}		
77 	foreach(i;0..frame.numRows)
78 	{
79 		string rowString="";
80 		foreach(j;0..frame.numCols)
81 			rowString~=frame[i,j].to!string~",";
82 		rowString=rowString[0..$-1]~"\n";
83 		ret~=rowString;
84 	}
85 	std.file.write(filename,ret);
86 	return frame;
87 }
88 
89 
90 
91 DataFrameTyped loadCSV(Malformed errorLevel=Malformed.ignore)(string data, string[] columnTitles=[], bool skipFirst=false,char separator=',')
92 {
93 	import std.string:strip;
94 	import std.range:enumerate;
95 	DataFrameTyped ret;
96 
97 //		auto csv=csvReader!(string, errorLevel)(data, separator, quote);
98 	auto csv=csvReader(data,separator); // , separator, quote);
99 	size_t i;
100 	auto peek=data.peekCols;
101 	bool hasHeader=(columnTitles.length==0);
102 	if (hasHeader)
103 		ret.setColumnTitles(peekHeaderCols(data,separator));
104 	else
105 	{
106 		ret.setColumnTitles(columnTitles);
107 		enforce(peek==columnTitles.length);
108 	}
109 	bool firstRow=true;
110 
111 	foreach(row;csv)
112 	{
113 		if (firstRow && (hasHeader || skipFirst))
114 		{
115 			firstRow=false;
116 			continue;
117 		}
118 		size_t j;
119 		ret.length=ret.length+1;
120 		bool f=false;
121 		foreach(k,col;enumerate(row))
122 		{
123 			if(k==0)
124 				continue;
125 			if(col.strip.length>0)
126 			{
127 				if(col.isNumeric)
128 				{
129 					if(col.to!double!=0.0)
130 					{
131 						f=true;
132 						break;
133 					}
134 				}
135 				else
136 				{
137 					f=true;
138 					break;
139 				}
140 			}
141 		}
142 		if (!f)
143 			continue;
144 		foreach(col;row)
145 		{
146 			string colName=columnTitles[j];
147 			final switch(ret.columnTypes[colName]) with(ColumnType)
148 			{
149 				case String:
150 					ret[i,colName]=col;
151 					break;
152 				case Int:
153 					ret[i,colName]=col.to!int;
154 					break;
155 				case Long:
156 					ret[i,colName]=col.to!long;
157 					break;
158 				case ColumnType.Date:
159 					ret.values.dates[colName][i]=parseDate!(std.datetime.Date)(col);
160 					break;
161 				case ColumnType.DateTime:
162 					ret.values.dateTimes[colName][i]=parseDate!(std.datetime.DateTime)(col);
163 					break;
164 				case Double:
165 					ret[i,colName]=(col.strip.length==0)?double.nan:col.to!double;
166 					break;
167 			}
168 			++j;
169 		}
170 		++i;
171 	}
172 	return ret;
173 }
174 
175 DataFrameTyped saveCSV(DataFrameTyped frame, string filename, bool useHeader=true)
176 {
177 	string ret;
178 	foreach(j;0..frame.numCols)
179 		ret~=frame.columnTitles[j]~",";
180 	ret=ret[0..$-1]~"\n";
181 	
182 	foreach(i;0..frame.numRows)
183 	{
184 		string rowString="";
185 		foreach(j;0..frame.numCols)
186 			rowString~=frame.loadCell!string(i,frame.columnTitles[j])~",";
187 		rowString=rowString[0..$-1]~"\n";
188 		ret~=rowString;
189 	}
190 	std.file.write(filename,ret);
191 	return frame;
192 }
193